package com.mk.exnotice.pojos;

import com.mk.exnotice.properties.ExceptionNoticeProperties;
import lombok.Cleanup;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.time.LocalDateTime;
import java.util.*;
import static java.util.stream.Collectors.toList;

@Data
public class ExceptionNotice {

    private String project;

    private String filterTrace;

    private String url;

    private String method;

    private Object[] args;

    private String argsStr;

    private String cause;

    private String message;

    private String classPath;

    private LocalDateTime createTime;

    private List<String> traceInfo = new ArrayList<>();

    private boolean sendError = true;

    public ExceptionNotice(Throwable ex, ExceptionNoticeProperties properties,String url, String method, Object[] args) {
        this.url = url;
        this.method = method;
        this.args = args;
        this.argsStr = String.join(",", Arrays.stream(args).map(x -> x.toString()).toArray(String[]::new));
        this.message = ex.getMessage();
        this.project = properties.getProjectName();
        this.createTime = LocalDateTime.now();

        if (StringUtils.isNotEmpty(properties.getIncludedTracePackage())) {
            List<StackTraceElement> list = stackTrace(ex, filterTrace);
            if (list.size() > 0) {
                this.traceInfo = list.stream().map(x -> x.toString()).collect(toList());
                this.cause =  String.join("\n",this.traceInfo.stream().map(x -> x.toString()).toArray(String[]::new));
                // this.method = list.get(0).getMethodName();
                this.classPath = list.get(0).getClassName();
            }
        } else {
            this.cause = getExceptionAllInfo(ex);
        }
    }

    public String getExceptionAllInfo(Throwable ex) {
        String exBody = "";
        try {
            @Cleanup
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            @Cleanup
            PrintStream pout = new PrintStream(out);
            ex.printStackTrace(pout);
            exBody = new String(out.toByteArray());
        } catch (Exception e) {
            return ex.getMessage();
        }

        return exBody;
    }


    private List<StackTraceElement> stackTrace(Throwable throwable, String filterTrace) {
        List<StackTraceElement> list = new LinkedList<StackTraceElement>();
        addStackTrace(list, throwable, filterTrace);
        Throwable cause = throwable.getCause();
        while (cause != null) {
            addStackTrace(list, cause, filterTrace);
            cause = cause.getCause();
        }
        return list;
    }

    public void addStackTrace(List<StackTraceElement> list, Throwable throwable, String filterTrace) {
        list.addAll(0,
                Arrays.stream(throwable.getStackTrace())
                        .filter(x -> filterTrace == null ? true : x.getClassName().startsWith(filterTrace))
                        .filter(x -> !x.getFileName().equals("<generated>")).collect(toList()));
    }
}
